﻿using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Xml.Linq;
using AzukiMap.Tools.Logger;

using AzukiMap.MapSource;
using AzukiMap.Map;
using System.Windows.Media;
using GeoAPI.Geometries;

namespace AzukiMap.Layer
{
    public class LayerManager
    {
        ObservableCollection<ILayer> _specialLayers = new ObservableCollection<ILayer>();

        private LocalVectorLayer _rangeLayer;
        private LocalVectorSource _rangeSource;
        public LocalVectorLayer RangeLayer
        {
            get { return _rangeLayer; }
        }
        public LocalVectorSource RangeSource
        {
            get { return _rangeSource; }
        }
        public void InitSpecialLayers(MapCoordinate mapCoordinate, MapMouseControl mapMouseControl, Canvas rootCanvas)
        {
            // TODO 4326 

            // for edit range mode (not recommended)
            _rangeSource = new LocalVectorSource("_range_", VectorSourceTypes.Polygon, 4326);
            _rangeLayer = new LocalVectorLayer(_rangeSource, "_range_")
                              {
                                  MaxScale = _rangeSource.MaxScale,
                                  Visibility = Visibility.Visible,
                                  MapCoord = mapCoordinate,
                                  DefaultStyle =
                                      {
                                          FillBrush = new SolidColorBrush(Color.FromArgb(1, 100, 150, 255)),
                                          Opacity = 0.05
                                      }
                              };

            // for edit vector mode (recommended)
            _editSource = new EditVectorSource("_edit_", VectorSourceTypes.Point, 4326);
            _editLayer = new EditVectorLayer(mapCoordinate, rootCanvas, _editSource, "_edit_")
                             {
                                 MaxScale = _editSource.MaxScale,
                                 Visibility = Visibility.Visible,
                                 MapCoord = mapCoordinate,
                                 ZIndex = 0,
                             };
            //_editLayer.ElementSelected += new VectorElementSelectedHandler(_editLayer_ElementSelected);

            //LocalVectorData data = new LocalVectorData();
            //data.Geometry = new GisSharpBlog.NetTopologySuite.Geometries.Point(135, 35);
            //_editSource.Add(data);

            _rangeLayer.ZIndex = 0;

            AddTop(_editLayer);
            _editLayer.Draw();
            AddTop(_rangeLayer);
            _rangeLayer.Draw();
            //_rangeLayer.ZIndex = int.MaxValue;
            //_editLayer.ZIndex = int.MaxValue;

            mapMouseControl.CheckFeatureDrag += new CheckFeatureDragHandler(mapMouseControl_CheckFeatureDrag);
        }

        bool mapMouseControl_CheckFeatureDrag(object sender)
        {
            return _editLayer.IsDrag;
        }

        private EditVectorLayer _editLayer;
        private EditVectorSource _editSource;
        public EditVectorLayer EditLayer
        {
            get
            {
                return _editLayer;
            }
        }
        public EditVectorSource EditSource
        {
            get { return _editSource; }
        }

        ObservableCollection<ILayer> _layers = new ObservableCollection<ILayer>();
        Canvas _layerContainer;

        public int LayerCount
        {
            get { return _layers.Count; }
        }

        public ObservableCollection<ILayer> Layers
        {
            set { _layers = value; }
            get { return _layers; }
        } 

        public LayerManager(Canvas layerContainer)
        {
            _layerContainer = layerContainer;
        }

        private bool drawable = true;
        public void SetLayerDrawable(bool drawable)
        {
            this.drawable = drawable;
            foreach (ILayer layer in _layers)
            {
                layer.IsDrawable = drawable;
            }
        }

        public void DrawAll()
        {
            foreach (ILayer layer in _layers)
            {
                layer.MoveEastWestCanvases();
                layer.Draw();
            }

            // for editable vector layer
            try
            {
                if (_editLayer!=null)
                    _editLayer.Draw();
                if (_rangeLayer != null)
                    _rangeLayer.Draw();
            }
            catch (Exception e)
            {
                System.Diagnostics.Debug.WriteLine(e.ToString());
            }

        }
            
        public void RemoveAllDrawings()
        {
            foreach (ILayer layer in _layers)
            {
                refresh(layer);
            }

            // for editable vector layer
            refresh(_editLayer);
            refresh(_rangeLayer);
        }

        public void refresh(ILayer layer)
        {
            if (layer == null) return;

            layer.Clear();
        }

        public void Add(ILayer layer)
        {
            AddTop(layer);
        }

        public void AddTop(ILayer layer)
        {
            Add(layer, 0);
        }

        public void AddBottom(ILayer layer)
        {
            Add(layer, _layers.Count);
        }

        public void Add(ILayer layer, int position)
        {
            if (hasSameName(layer))
            {
                Logger.Error("Cannot add a layer has a existing layer name.");
                return;
            }
            if (position < 0)
            {
                position = 0;
            }
            if (position >= _layers.Count)
            {
                _layers.Add(layer);
                _layerContainer.Children.Add((UIElement)layer); 
            }
            else
            {
                _layers.Insert(position, layer);
                _layerContainer.Children.Insert(position, (UIElement)layer);
            }

            layer.IsDrawable = drawable;

            layer.Draw();

            SetZIndex();
        }


        public void Remove(ILayer layer)
        {
            if ( CheckLayerBelongs(layer)) {
                _layers.Remove(layer);
                layer.Clear();
                _layerContainer.Children.Remove((MapLayer)layer);

                SetZIndex();
            }
        }

        public void RemoveAll()
        {
            while (_layers.Count > 0)
            {
                ILayer layer = _layers.First();
                Remove(layer);
            }
        }

        public int MoveUp(ILayer layer)
        {
            return Move(layer, -1); 
        }

        public int MoveDown(ILayer layer)
        {
            return Move(layer, 1);
        }

        public int Move(ILayer layer, int moveCount)
        {
            if ( ! CheckLayerBelongs(layer)) {
                return -1;
            }

            int currentOrder = _layers.IndexOf(layer);
            int listCount = _layers.Count;

            if (moveCount == 0)
            {
                return currentOrder;
            }

            int newOrder = currentOrder + moveCount;
            if (newOrder <= 0)
            {
                newOrder = 0;
            }
            if (newOrder > listCount - 1)
            {
                newOrder = listCount - 1;
            }
            //int newOrder = currentOrder + moveCount;

            _layers.Remove(layer);
            _layers.Insert(newOrder, layer);

            SetZIndex();

            return newOrder;
        }

        /// <summary>
        /// Move a layer as the top.
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public int MoveTop(ILayer layer)
        {
            var newOrder = _layers.Count;
            layer.ZIndex = newOrder;
            return newOrder;
        }

        public ILayer GetLayer(string layerName)
        {
            foreach (ILayer layer in _layers)
            {
                if ( layer.GetType() == typeof(LayerGroup) )
                {
                    ILayer gLayer = GetLayerFromLayerGroup((LayerGroup)layer, layerName);
                    if ( gLayer != null )
                    {
                        return gLayer;
                    }
                }
                if (layer.LayerName == layerName)
                {
                    return layer;
                }
            }
            return null;
        }
       
        ILayer GetLayerFromLayerGroup(LayerGroup group, string layerName)
        {
            foreach (var layer in group.Layers)
            {
                if ( layer.GetType() == typeof(LayerGroup) )
                {
                    ILayer gLayer = GetLayerFromLayerGroup((LayerGroup)layer, layerName);
                    if ( gLayer != null )
                    {
                        return gLayer;
                    }
                }
                if ( layer.LayerName == layerName )
                {
                    return layer;
                }
            }
            return null;
        }

        public void Show(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                layer.Show();
                //ShowLayer(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void Hide(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                layer.Hide();
                //HideLayer(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void Remove(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                Remove(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void MoveUp(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                MoveUp(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public void MoveDown(string layerName)
        {
            try
            {
                ILayer layer = GetLayer(layerName);
                MoveDown(layer);
            }
            catch (ArgumentException ex)
            {
                Logger.Error("Layer '" + layerName + "' is not available. \n" 
                    + ex.Message + "\n" + ex.StackTrace );
            }
        }

        public XElement ToXml() {
            XElement layersXml = new XElement("Layers",
                from layer in _layers
                select layer.ToXml()
                );
            return layersXml;
        }

        bool hasSameName(ILayer layer)
        {
            bool hasSame = false;
            foreach (ILayer l in _layers)
            {
                if (l.LayerName == layer.LayerName)
                {
                    hasSame = true;
                    break;
                }
            }
            return hasSame;
        }

        bool CheckLayerBelongs(ILayer layer) 
        {
            if (_layers.Contains(layer)) {
                return true;
            } else {
                Logger.Warn("Layer '" + layer.LayerName + "' does not belongs to the layer manager.");
                return false;
            }
        }

        void SetZIndex()
        {
            foreach (ILayer layer in _layers)
            {
                int layerOrder = _layers.Count - _layers.IndexOf(layer);
                layer.ZIndex = layerOrder;
            }

            MoveTop(_editLayer);
            MoveTop(_rangeLayer);
        }

        void _editLayer_ElementSelected(VectorLayer layer, int id, System.Windows.Shapes.Shape element)
        {
            EditLayer.EditVectorMode = EditVectorMode.Edit;
        }

    }
}
